Completed
Pull Request — master (#328)
by
unknown
02:45
created

View._initSelection   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 12
rs 9.2
1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global Handlebars, Gallery, GalleryImage, Thumbnails */
13
(function ($, _, OC, t, Gallery) {
14
	"use strict";
15
16
	var TEMPLATE_ADDBUTTON = '<a href="#" class="button new"><span class="icon icon-add"></span><span class="hidden-visually">New</span></a>';
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
17
	var TEMPLATE_DOWNLOADBUTTON = '<a href="#" id="download-selected-button" class="button download hidden"><span class="icon icon-download"></span>' +
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
18
		'<span class="hidden-visually">Download</span></a>';
19
20
	/**
21
	 * Builds and updates the Gallery view
22
	 *
23
	 * @constructor
24
	 */
25
	var View = function () {
26
		this.element = $('#gallery');
27
		this.loadVisibleRows.loading = false;
28
		this._setupUploader();
29
		this.breadcrumb = new Gallery.Breadcrumb();
30
		this.emptyContentElement = $('#emptycontent');
31
		this.controlsElement = $('#controls');
32
	};
33
34
	View.prototype = {
35
		element: null,
36
		breadcrumb: null,
37
		requestId: -1,
38
		emptyContentElement: null,
39
		controlsElement: null,
40
		/**
41
		 * Map of file id to file data
42
		 * @type Object.<int, Object>
43
		 */
44
		_selectedFiles: {},
45
		/**
46
		 * Summary of selected files.
47
		 * @type OCA.Files.FileSummary
48
		 */
49
		_selectionSummary: null,
50
		/**
51
		 * Initialiation status
52
		 * @type Boolean
53
		 */
54
		_initialized: false,
55
56
		/**
57
		 * Removes all thumbnails from the view
58
		 */
59
		clear: function () {
60
			this.loadVisibleRows.processing = false;
61
			this.loadVisibleRows.loading = null;
62
			// We want to keep all the events
63
			this.element.children().detach();
64
			this.showLoading();
65
		},
66
67
		/**
68
		 * @param {string} path
69
		 * @returns {boolean}
70
		 */
71
		_isValidPath: function(path) {
72
			var sections = path.split('/');
73
			for (var i = 0; i < sections.length; i++) {
74
				if (sections[i] === '..') {
75
					return false;
76
				}
77
			}
78
79
			return path.toLowerCase().indexOf(decodeURI('%0a')) === -1 &&
80
				path.toLowerCase().indexOf(decodeURI('%00')) === -1;
81
		},
82
83
		/**
84
		 * Populates the view if there are images or albums to show
85
		 *
86
		 * @param {string} albumPath
87
		 * @param {string|undefined} errorMessage
88
		 */
89
		init: function (albumPath, errorMessage) {
90
			// Set path to an empty value if not a valid one
91
			if(!this._isValidPath(albumPath)) {
92
				albumPath = '';
93
			}
94
95
			// Only do it when the app is initialised
96
			if (this.requestId === -1) {
97
				this._initButtons();
98
				this._blankUrl();
99
			}
100
			if ($.isEmptyObject(Gallery.imageMap)) {
101
				Gallery.view.showEmptyFolder(albumPath, errorMessage);
102
			} else {
103
				this.viewAlbum(albumPath);
104
			}
105
106
			this._setBackgroundColour();
107
108
			this._initSelection();
109
			this.initialized = true;
110
		},
111
112
		/**
113
		 * Starts the slideshow
114
		 *
115
		 * @param {string} path
116
		 * @param {string} albumPath
117
		 */
118
		startSlideshow: function (path, albumPath) {
119
			var album = Gallery.albumMap[albumPath];
120
			var images = album.images;
121
			var startImage = Gallery.imageMap[path];
122
			Gallery.slideShow(images, startImage, false);
123
		},
124
125
		/**
126
		 * Sets up the controls and starts loading the gallery rows
127
		 *
128
		 * @param {string|null} albumPath
129
		 */
130
		viewAlbum: function (albumPath) {
131
			albumPath = albumPath || '';
132
			if (!Gallery.albumMap[albumPath]) {
133
				return;
134
			}
135
136
			this.clear();
137
138
			if (albumPath !== Gallery.currentAlbum
139
				|| (albumPath === Gallery.currentAlbum &&
140
				Gallery.albumMap[albumPath].etag !== Gallery.currentEtag)) {
141
				Gallery.currentAlbum = albumPath;
142
				Gallery.currentEtag = Gallery.albumMap[albumPath].etag;
143
				this._setupButtons(albumPath);
144
			}
145
146
			Gallery.albumMap[albumPath].viewedItems = 0;
147
			Gallery.albumMap[albumPath].preloadOffset = 0;
148
149
			// Each request has a unique ID, so that we can track which request a row belongs to
150
			this.requestId = Math.random();
151
			Gallery.albumMap[Gallery.currentAlbum].requestId = this.requestId;
152
153
			// Loading rows without blocking the execution of the rest of the script
154
			setTimeout(function () {
155
				this.loadVisibleRows.activeIndex = 0;
156
				this.loadVisibleRows(Gallery.albumMap[Gallery.currentAlbum]);
157
			}.bind(this), 0);
158
		},
159
160
		/**
161
		 * Manages the sorting interface
162
		 *
163
		 * @param {string} sortType name or date
164
		 * @param {string} sortOrder asc or des
165
		 */
166
		sortControlsSetup: function (sortType, sortOrder) {
167
			var reverseSortType = 'date';
168
			if (sortType === 'date') {
169
				reverseSortType = 'name';
170
			}
171
			this._setSortButton(sortType, sortOrder, true);
172
			this._setSortButton(reverseSortType, 'asc', false); // default icon
173
		},
174
175
		/**
176
		 * Loads and displays gallery rows on screen
177
		 *
178
		 * view.loadVisibleRows.loading holds the Promise of a row
179
		 *
180
		 * @param {Album} album
181
		 */
182
		loadVisibleRows: function (album) {
183
			var view = this;
184
			// Wait for the previous request to be completed
185
			if (this.loadVisibleRows.processing) {
186
				return;
187
			}
188
189
			/**
190
			 * At this stage, there is no loading taking place, so we can look for new rows
191
			 */
192
193
			var scroll = $('#content-wrapper').scrollTop() + $(window).scrollTop();
194
			// 2 windows worth of rows is the limit from which we need to start loading new rows.
195
			// As we scroll down, it grows
196
			var targetHeight = ($(window).height() * 2) + scroll;
197
			// We throttle rows in order to try and not generate too many CSS resizing events at
198
			// the same time
199
			var showRows = _.throttle(function (album) {
200
201
				// If we've reached the end of the album, we kill the loader
202
				if (!(album.viewedItems < album.subAlbums.length + album.images.length)) {
0 ignored issues
show
Coding Style introduced by
The usage of ! looks confusing here.

The following shows a case which JSHint considers confusing and its respective non-confusing counterpart:

! (str.indexOf(i) > -1) // Bad
str.indexOf(i) === -1 // Good
Loading history...
203
					view.loadVisibleRows.processing = false;
204
					view.loadVisibleRows.loading = null;
205
					return;
206
				}
207
208
				// Prevents creating rows which are no longer required. I.e when changing album
209
				if (view.requestId !== album.requestId) {
210
					return;
211
				}
212
213
				// We can now safely create a new row
214
				var row = album.getRow($(window).width());
215
				var rowDom = row.getDom();
216
				view.element.append(rowDom);
217
218
				return album.fillNextRow(row).then(function () {
219
					if (album.viewedItems < album.subAlbums.length + album.images.length &&
220
						view.element.height() < targetHeight) {
221
						return showRows(album);
222
					}
223
					// No more rows to load at the moment
224
					view.loadVisibleRows.processing = false;
225
					view.loadVisibleRows.loading = null;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
226
				}, function () {
227
					// Something went wrong, so kill the loader
228
					view.loadVisibleRows.processing = false;
229
					view.loadVisibleRows.loading = null;
230
				});
231
			}, 100);
232
			if (this.element.height() < targetHeight) {
233
				this._showNormal();
234
				this.loadVisibleRows.processing = true;
235
				album.requestId = view.requestId;
236
				this.loadVisibleRows.loading = showRows(album);
237
			}
238
		},
239
240
		/**
241
		 * Shows an empty gallery message
242
		 *
243
		 * @param {string} albumPath
244
		 * @param {string|null} errorMessage
245
		 */
246
		showEmptyFolder: function (albumPath, errorMessage) {
247
			var message = '<div class="icon-gallery"></div>';
248
			var uploadAllowed = true;
249
250
			this.element.children().detach();
251
			this.removeLoading();
252
253
			if (!_.isUndefined(errorMessage) && errorMessage !== null) {
254
				message += '<h2>' + t('gallery',
255
						'Album cannot be shown') + '</h2>';
256
				message += '<p>' + escapeHTML(errorMessage) + '</p>';
0 ignored issues
show
Bug introduced by
escapeHTML does not seem to be defined.
Loading history...
257
				uploadAllowed = false;
258
			} else {
259
				message += '<h2>' + t('gallery',
260
						'No media files found') + '</h2>';
261
				// We can't upload yet on the public side
262
				if (Gallery.token) {
263
					message += '<p>' + t('gallery',
264
							'Upload pictures in the Files app to display them here') + '</p>';
265
				} else {
266
					message += '<p>' + t('gallery',
267
							'Upload new files via drag and drop or by using the [+] button above') +
268
						'</p>';
269
				}
270
			}
271
			this.emptyContentElement.html(message);
272
			this.emptyContentElement.removeClass('hidden');
273
274
			this._hideButtons(uploadAllowed);
275
			Gallery.currentAlbum = albumPath;
276
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
277
			this.breadcrumb.init(albumPath, availableWidth);
278
			Gallery.config.albumDesign = null;
279
		},
280
281
		/**
282
		 * Dims the controls bar when retrieving new content. Matches the effect in Files
283
		 */
284
		dimControls: function () {
285
			// Use the existing mask if its already there
286
			var $mask = this.controlsElement.find('.mask');
287
			if ($mask.exists()) {
288
				return;
289
			}
290
			$mask = $('<div class="mask transparent"></div>');
291
			this.controlsElement.append($mask);
292
			$mask.removeClass('transparent');
293
		},
294
295
		/**
296
		 * Shows the infamous loading spinner
297
		 */
298
		showLoading: function () {
299
			this.emptyContentElement.addClass('hidden');
300
			this.controlsElement.removeClass('hidden');
301
			$('#content').addClass('icon-loading');
302
			this.dimControls();
303
		},
304
305
		/**
306
		 * Removes the spinner in the main area and restore normal visibility of the controls bar
307
		 */
308
		removeLoading: function () {
309
			$('#content').removeClass('icon-loading');
310
			this.controlsElement.find('.mask').remove();
311
		},
312
313
		/**
314
		 * Shows thumbnails
315
		 */
316
		_showNormal: function () {
317
			this.emptyContentElement.addClass('hidden');
318
			this.controlsElement.removeClass('hidden');
319
			this.removeLoading();
320
		},
321
322
		/**
323
		 * Sets up our custom handlers for folder uploading operations
324
		 *
325
		 * @see OC.Upload.init/file_upload_param.done()
326
		 *
327
		 * @private
328
		 */
329
		_setupUploader: function () {
330
			var $uploadEl = $('#file_upload_start');
331
			if (!$uploadEl.exists()) {
332
				return;
333
			}
334
			this._uploader = new OC.Uploader($uploadEl, {
335
				fileList: FileList,
0 ignored issues
show
Bug introduced by
The variable FileList seems to be never declared. If this is a global, consider adding a /** global: FileList */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
336
				dropZone: $('#content')
337
			});
338
			this._uploader.on('add', function (e, data) {
339
				data.targetDir = '/' + Gallery.currentAlbum;
340
			});
341
			this._uploader.on('done', function (e, upload) {
342
				var data = upload.data;
343
344
				// is that the last upload ?
345
				if (data.files[0] === data.originalFiles[data.originalFiles.length - 1]) {
346
					var fileList = data.originalFiles;
347
					//Ask for a refresh of the photowall
348
					Gallery.getFiles(Gallery.currentAlbum).done(function () {
349
						var fileId, path;
350
						// Removes the cached thumbnails of files which have been re-uploaded
351
						_(fileList).each(function (fileName) {
352
							path = Gallery.currentAlbum + '/' + fileName;
353
							if (Gallery.imageMap[path]) {
354
								fileId = Gallery.imageMap[path].fileId;
355
								if (Thumbnails.map[fileId]) {
356
									delete Thumbnails.map[fileId];
357
								}
358
							}
359
						});
360
361
						Gallery.view.init(Gallery.currentAlbum);
362
					});
363
				}
364
			});
365
366
			// Since Nextcloud 9.0
367
			if (OC.Uploader) {
368
				OC.Uploader.prototype._isReceivedSharedFile = function (file) {
369
					var path = file.name;
370
					var sharedWith = false;
371
372
					if (Gallery.currentAlbum !== '' && Gallery.currentAlbum !== '/') {
373
						path = Gallery.currentAlbum + '/' + path;
374
					}
375
					if (Gallery.imageMap[path] && Gallery.imageMap[path].sharedWithUser) {
376
						sharedWith = true;
377
					}
378
379
					return sharedWith;
380
				};
381
			}
382
		},
383
384
		/**
385
		 * Setups selection feature
386
		 * Also resets file actions list to be hidden in case of user navigating
387
		 * back and forth between albums
388
		 *
389
		 * @private
390
		 */
391
		_initSelection: function() {
392
			this._selectedFiles = {};
393
			this._selectionSummary = new OCA.Files.FileSummary();
394
			if (!this.initialized) {
395
				this.element.on('change', '.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
396
			}
397
398
			var downloadSelectedButton = $('#download-selected-button');
399
			if (downloadSelectedButton && !downloadSelectedButton.hasClass('hidden')) {
400
				downloadSelectedButton.addClass('hidden');
401
			}
402
		},
403
404
		/**
405
		 * Adds all the click handlers to buttons the first time they appear in the interface
406
		 *
407
		 * @private
408
		 */
409
		_initButtons: function () {
410
			this.element.on("contextmenu", function(e) { e.preventDefault(); });
411
			$('#filelist-button').click(Gallery.switchToFilesView);
412
			$('#download').click(Gallery.download);
413
			$('#shared-button').click(Gallery.share);
414
			Gallery.infoBox = new Gallery.InfoBox();
415
			$('#album-info-button').click(Gallery.showInfo);
416
			$('#sort-name-button').click(Gallery.sorter);
417
			$('#sort-date-button').click(Gallery.sorter);
418
			$('#save #save-button').click(Gallery.showSaveForm);
419
			$('.save-form').submit(Gallery.saveForm);
420
			this._renderDownloadSelectedButton();
421
			this._renderNewButton();
422
			// Trigger cancelling of file upload
423
			$('#uploadprogresswrapper .stop').on('click', function () {
424
				OC.Upload.cancelUploads();
425
			});
426
			this.requestId = Math.random();
427
		},
428
429
		/**
430
		 * Sets up all the buttons of the interface and the breadcrumbs
431
		 *
432
		 * @param {string} albumPath
433
		 * @private
434
		 */
435
		_setupButtons: function (albumPath) {
436
			this._shareButtonSetup(albumPath);
437
			this._infoButtonSetup();
438
439
			var availableWidth = $(window).width() - Gallery.buttonsWidth;
440
			this.breadcrumb.init(albumPath, availableWidth);
441
			var album = Gallery.albumMap[albumPath];
442
443
			var sum = album.images.length + album.subAlbums.length;
444
			//If sum of the number of images and subalbums exceeds 1 then show the buttons.
445
			if(sum > 1)
446
			{
447
				$('#sort-name-button').show();
448
				$('#sort-date-button').show();
449
			}
450
			else
451
			{
452
				$('#sort-name-button').hide();
453
				$('#sort-date-button').hide();
454
			}
455
			var currentSort = Gallery.config.albumSorting;
456
			this.sortControlsSetup(currentSort.type, currentSort.order);
457
			Gallery.albumMap[Gallery.currentAlbum].images.sort(
458
				Gallery.utility.sortBy(currentSort.type,
459
					currentSort.order));
460
			Gallery.albumMap[Gallery.currentAlbum].subAlbums.sort(Gallery.utility.sortBy('name',
461
				currentSort.albumOrder));
462
463
			$('#save-button').show();
464
			$('#download').show();
465
			$('a.button.new').show();
466
		},
467
468
		/**
469
		 * Hide buttons in the controls bar
470
		 *
471
		 * @param uploadAllowed
472
		 */
473
		_hideButtons: function (uploadAllowed) {
474
			$('#album-info-button').hide();
475
			$('#shared-button').hide();
476
			$('#sort-name-button').hide();
477
			$('#sort-date-button').hide();
478
			$('#save-button').hide();
479
			$('#download').hide();
480
481
			if (!uploadAllowed) {
482
				$('a.button.new').hide();
483
			}
484
		},
485
486
		/**
487
		 * Shows or hides the share button depending on if we're in a public gallery or not
488
		 *
489
		 * @param {string} albumPath
490
		 * @private
491
		 */
492
		_shareButtonSetup: function (albumPath) {
493
			var shareButton = $('#shared-button');
494
			if (albumPath === '' || Gallery.token) {
495
				shareButton.hide();
496
			} else {
497
				shareButton.show();
498
			}
499
		},
500
501
		/**
502
		 * Shows or hides the info button based on the information we've received from the server
503
		 *
504
		 * @private
505
		 */
506
		_infoButtonSetup: function () {
507
			var infoButton = $('#album-info-button');
508
			infoButton.find('span').hide();
509
			var infoContentContainer = $('.album-info-container');
510
			infoContentContainer.slideUp();
511
			infoContentContainer.css('max-height',
512
				$(window).height() - Gallery.browserToolbarHeight);
513
			var albumInfo = Gallery.config.albumInfo;
514
			if (Gallery.config.albumError) {
515
				infoButton.hide();
516
				var text = '<strong>' + t('gallery', 'Configuration error') + '</strong></br>' +
517
					Gallery.config.albumError.message + '</br></br>';
518
				Gallery.utility.showHtmlNotification(text, 7);
519
			} else if ($.isEmptyObject(albumInfo)) {
520
				infoButton.hide();
521
			} else {
522
				infoButton.show();
523
				if (albumInfo.inherit !== 'yes' || albumInfo.level === 0) {
524
					infoButton.find('span').delay(1000).slideDown();
525
				}
526
			}
527
		},
528
529
		/**
530
		 * Sets the background colour of the photowall
531
		 *
532
		 * @private
533
		 */
534
		_setBackgroundColour: function () {
535
			var wrapper = $('#content-wrapper');
536
			var albumDesign = Gallery.config.albumDesign;
537
			if (!$.isEmptyObject(albumDesign) && albumDesign.background) {
538
				wrapper.css('background-color', albumDesign.background);
539
			} else {
540
				wrapper.css('background-color', '#fff');
541
			}
542
		},
543
544
		/**
545
		 * Picks the image which matches the sort order
546
		 *
547
		 * @param {string} sortType name or date
548
		 * @param {string} sortOrder asc or des
549
		 * @param {boolean} active determines if we're setting up the active sort button
550
		 * @private
551
		 */
552
		_setSortButton: function (sortType, sortOrder, active) {
553
			var button = $('#sort-' + sortType + '-button');
554
			// Removing all the classes which control the image in the button
555
			button.removeClass('active');
556
			button.find('img').removeClass('front');
557
			button.find('img').removeClass('back');
558
559
			// We need to determine the reverse order in order to send that image to the back
560
			var reverseSortOrder = 'des';
561
			if (sortOrder === 'des') {
562
				reverseSortOrder = 'asc';
563
			}
564
565
			// We assign the proper order to the button images
566
			button.find('img.' + sortOrder).addClass('front');
567
			button.find('img.' + reverseSortOrder).addClass('back');
568
569
			// The active button needs a hover action for the flip effect
570
			if (active) {
571
				button.addClass('active');
572
				if (button.is(":hover")) {
573
					button.removeClass('hover');
574
				}
575
				// We can't use a toggle here
576
				button.hover(function () {
577
						$(this).addClass('hover');
578
					},
579
					function () {
580
						$(this).removeClass('hover');
581
					});
582
			}
583
		},
584
585
		/**
586
		 * If no url is entered then do not show the error box.
587
		 *
588
		 */
589
		_blankUrl: function() {
590
			$('#remote_address').on("change keyup paste", function() {
591
 				if ($(this).val() === '') {
592
 					$('#save-button-confirm').prop('disabled', true);
593
 				} else {
594
 					$('#save-button-confirm').prop('disabled', false);
595
 				}
596
			});
597
		},
598
599
		/**
600
		 * Creates the [+] button allowing users who can't drag and drop to upload files
601
		 *
602
		 * @see core/apps/files/js/filelist.js
603
		 * @private
604
		 */
605
		_renderNewButton: function () {
606
			// if no actions container exist, skip
607
			var $actionsContainer = $('.actions.creatable');
608
			if (!$actionsContainer.length) {
609
				return;
610
			}
611
			if (!this._addButtonTemplate) {
612
				this._addButtonTemplate = Handlebars.compile(TEMPLATE_ADDBUTTON);
613
			}
614
			var $newButton = $(this._addButtonTemplate({
615
				addText: t('gallery', 'New'),
616
				iconUrl: OC.imagePath('core', 'actions/add')
617
			}));
618
619
			$actionsContainer.prepend($newButton);
620
			$newButton.tooltip({'placement': 'bottom'});
621
622
			$newButton.click(_.bind(this._onClickNewButton, this));
623
			this._newButton = $newButton;
624
		},
625
626
		/**
627
		 * Creates the click handler for the [+] button
628
		 * @param event
629
		 * @returns {boolean}
630
		 *
631
		 * @see core/apps/files/js/filelist.js
632
		 * @private
633
		 */
634
		_onClickNewButton: function (event) {
635
			var $target = $(event.target);
636
			if (!$target.hasClass('.button')) {
637
				$target = $target.closest('.button');
638
			}
639
			this._newButton.tooltip('hide');
640
			event.preventDefault();
641
			if ($target.hasClass('disabled')) {
642
				return false;
643
			}
644
			if (!this._newFileMenu) {
645
				this._newFileMenu = new Gallery.NewFileMenu();
646
				$('.actions').append(this._newFileMenu.$el);
647
			}
648
			this._newFileMenu.showAt($target);
649
650
			if (Gallery.currentAlbum === '') {
651
				$('.menuitem[data-action="hideAlbum"]').parent().hide();
652
			}
653
			return false;
654
		},
655
656
		/**
657
		 * Creates the download individual files button
658
		 *
659
		 * @see core/apps/files/js/filelist.js
660
		 * @private
661
		 */
662
		_renderDownloadSelectedButton: function () {
663
			if (!this._downloadButtonTemplate) {
664
				this._downloadButtonTemplate = Handlebars.compile(TEMPLATE_DOWNLOADBUTTON);
665
			}
666
			var $downloadButton = $(this._downloadButtonTemplate({
667
				addText: t('gallery', 'Download'),
668
				iconUrl: OC.imagePath('core', 'actions/download')
669
			}));
670
671
			$('#filelist-button').before($downloadButton);
672
			$downloadButton.tooltip({'placement': 'bottom'});
673
674
			$downloadButton.click(_.bind(this._onClickDownloadSelected, this));
675
		},
676
677
		/**
678
		 * Event handler for when clicking on "Download" for the selected files
679
		 *
680
		 * @see core/apps/files/js/filelist.js
681
		 * @private
682
		 */
683
		_onClickDownloadSelected: function(event) {
684
			var files;
685
			var dir = Gallery.currentAlbum;
686
			if (dir === '') {
687
				dir = '/';
688
			}
689
			files = _.pluck(this.getSelectedFiles(), 'path');
690
			files.forEach(function(file, index, files) {
691
				files[index] = OC.basename(file);
692
			});
693
694
			var downloadFileActionIcon = $('#download-selected-button .icon');
695
696
			// don't allow a second click on the download action
697
			if (downloadFileActionIcon.hasClass('icon-loading-small')) {
698
				event.preventDefault();
699
				return;
700
			}
701
702
			var disableLoadingState = function() {
703
				downloadFileActionIcon.removeClass('icon-loading-small');
704
				downloadFileActionIcon.addClass('icon-download');
705
			};
706
			downloadFileActionIcon.removeClass('icon-download');
707
			downloadFileActionIcon.addClass('icon-loading-small');
708
709
			if(this.getSelectedFiles().length > 1) {
710
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(files, dir), disableLoadingState);
711
			}
712
			else {
713
				var first = OC.basename(this.getSelectedFiles()[0].path);
714
				OCA.Files.Files.handleDownload(Gallery.getSelectionDownloadUrl(first, dir), disableLoadingState);
715
			}
716
			return false;
717
	},
718
719
		/**
720
		 * Returns the file info of the selected files
721
		 *
722
		 * @return array of file names
723
		 *
724
		 * @see core/apps/files/js/filelist.js
725
		 * @private
726
		 */
727
		getSelectedFiles: function() {
728
			return _.values(this._selectedFiles);
729
		},
730
731
		/**
732
		 * Returns the file data from a given file element.
733
		 * @param $el file tr element
734
		 * @return file data
735
		 *
736
		 * @see core/apps/files/js/filelist.js
737
		 * @private
738
		 */
739
		elementToFile: function($el){
740
			$el = $($el);
741
			var data = {
742
				id: parseInt($el.attr('data-id'), 10),
743
			};
744
			var path = $el.attr('data-path');
745
			if (path) {
746
				data.path = path;
747
			}
748
			return data;
749
		},
750
751
		/**
752
		 * Selected/deselects the given file element and updated
753
		 * the internal selection cache.
754
		 *
755
		 * @param {Object} $element single image
756
		 * @param {bool} state true to select, false to deselect
757
		 *
758
		 * @see core/apps/files/js/filelist.js
759
		 * @private
760
		 */
761
		_selectFileEl: function($element, state) {
762
			var $checkbox = $element.find('row-element>.image-label>.selectCheckBox');
763
			var oldData = !!this._selectedFiles[$element.data('id')];
764
			var data;
765
			$checkbox.prop('checked', state);
766
			$element.toggleClass('selected', state);
767
			// already selected ?
768
			if (state === oldData) {
769
				return;
770
			}
771
			data = this.elementToFile($element);
772
			if (state) {
773
				this._selectedFiles[$element.data('id')] = data;
774
				this._selectionSummary.add(data);
775
			}
776
			else {
777
				delete this._selectedFiles[$element.data('id')];
778
				this._selectionSummary.remove(data);
779
			}
780
		},
781
782
		/**
783
		 * Event handler for when clicking on a element's checkbox
784
		 *
785
		 * @see core/apps/files/js/filelist.js
786
		 * @private
787
		 */
788
		_onClickFileCheckbox: function(event) {
789
			var $image = $(event.target).closest('.' + GalleryImage.cssClass);
790
			var state = !$image.hasClass('selected');
791
			this._selectFileEl($image, state);
792
			this._lastChecked = $image;
793
			this.updateSelectionSummary();
794
		},
795
796
		/**
797
		 * Update UI based on the current selection
798
		 *
799
		 * @see core/apps/files/js/filelist.js
800
		 * @private
801
		 */
802
		updateSelectionSummary: function() {
803
			var summary = this._selectionSummary.summary;
804
805
			if (summary.totalFiles === 0 && summary.totalDirs === 0) {
806
				$('#download-selected-button').addClass('hidden');
807
			}
808
			else {
809
				$('#download-selected-button').removeClass('hidden');
810
			}
811
		},
812
813
	};
814
815
	Gallery.View = View;
816
})(jQuery, _, OC, t, Gallery);
817